package core

import (
	"bytes"
	"encoding/json"
	"gitee.com/big_meteor/godash/validate"
	"github.com/kataras/iris/v12"
	"github.com/kataras/iris/v12/context"
	"io/ioutil"
	"net/url"
	"reflect"
	"strconv"
	"strings"
	"time"
)

type Context struct {
	iris.Context
}

// Handle 转化为iris中间件
func Handle(f func(ctx Context)) func(iris.Context) {
	return func(ctx iris.Context) {
		c := Context{ctx}
		f(c)
	}
}

// Handles 转化为iris中间件
func Handles(f ...func(ctx Context)) []context.Handler {
	_f := make([]context.Handler, len(f))

	if f == nil || len(f) == 0 {
		return _f
	}

	for i, _ := range f {
		_f[i] = Handle(f[i])
	}
	return _f
}

// Verify 校验参数，若参数为空则自动检验json参数
func (c Context) Verify(v ...interface{}) error {
	var _v interface{}

	if v == nil || len(v) == 0 {
		return NewError(Fail, "校验并未找到相关参数")
	}

	_v = v[0]

	rt := reflect.TypeOf(_v)
	rv := reflect.ValueOf(_v)

	if rv.Kind() == reflect.Ptr {
		rv = rv.Elem()
		rt = reflect.TypeOf(rv.Interface())
	}

	fieldsLen := rv.NumField()

	for i := 0; i < fieldsLen; i++ {
		var value interface{}
		value = rv.Field(i).Interface()
		typeFiled := rt.Field(i)

		verifyTag := typeFiled.Tag.Get("verify")

		if verifyTag != "" {
			msgTag := typeFiled.Tag.Get("msg")
			codeTag := typeFiled.Tag.Get("code")
			jsonTag := typeFiled.Tag.Get("json")

			if codeTag == "" {
				codeTag = "400"
			}
			status, err := strconv.ParseInt(codeTag, 10, 32)
			if err != nil {
				return NewError(ServerError, typeFiled.Name+"的code标签设置不正确")
			}
			success, err := validate.Check(value, verifyTag)
			if err != nil {
				return NewError(ServerError, err.Error())
			}
			if !success {
				if msgTag != "" {
					return NewError(int(status), msgTag)
				}
				return NewError(int(status), jsonTag+"验证失败")
			}
		}
	}
	return nil
}

const body = "__this is the key to body__"
const query = "__this is the key to query__"

// Read 将 json 参数转化为 dto
func (c Context) ReadBody(v interface{}) error {
	c.Context.Values().Set(body, v)

	_body := c.Request().Body
	buf, err := ioutil.ReadAll(_body)
	if err != nil {
		return err
	}
	// 读取完毕关闭body
	err = _body.Close()
	if err != nil {
		return err
	}
	// 将body数据还原到本次请求上，以便后续中间件使用原生函数进行处理
	c.Request().Body = ioutil.NopCloser(bytes.NewBuffer(buf))
	// 空值控制
	if len(buf) == 0 {
		buf = []byte("{}")
	}
	err = json.Unmarshal(buf, v)
	if err != nil {
		return err
	}

	return nil
}

// GetBody 获取Body参数
func (c Context) GetBody() interface{} {
	_params := c.Context.Values().Get(body)
	if _params != nil {
		return reflect.ValueOf(_params).Elem().Interface()
	}

	return nil
}

// ReadQuery 读取query参数
func (c Context) ReadQuery(v interface{}) error {
	c.Context.Values().Set(query, v)
	m := c.GetQueryMap()
	buf, err := json.Marshal(m)
	if err != nil {
		return NewError(Fail, err.Error())
	}
	err = json.Unmarshal(buf, v)
	if err != nil {
		return NewError(Fail, err.Error())
	}
	return nil
}

// GetQuery 获取query参数
func (c Context) GetQuery() interface{} {
	_params := c.Context.Values().Get(query)
	if _params != nil {
		return reflect.ValueOf(_params).Elem().Interface()
	}

	return nil
}

// GetQueryMap 获取query参数map
func (c Context) GetQueryMap() map[string]string {
	query := c.Context.Request().URL.Query().Encode()
	query, _ = url.QueryUnescape(query)
	m := make(map[string]string)
	if query == "" {
		return m
	}
	queries := strings.Split(query, "&")

	for _, v := range queries {
		kv := strings.Split(v, "=")
		m[kv[0]] = kv[1]
	}
	return m
}

// ReadBodyVerify 读取body参数并检验
func (c Context) ReadBodyVerify(v interface{}) error {
	err := c.ReadBody(v)
	if err != nil {
		return err
	}
	err = c.Verify(v)
	return err
}

// ReadQueryVerify 读取body参数并检验
func (c Context) ReadQueryVerify(v interface{}) error {
	err := c.ReadQuery(v)
	if err != nil {
		return err
	}
	err = c.Verify(v)
	return err
}

// Response 网络返回
func (c Context) Response(status int, message string, data ...interface{}) (int, error) {
	m := iris.Map{
		"status":    status,
		"message":   message,
		"timestamp": time.Now(),
	}

	if data != nil && len(data) != 0 {
		m["data"] = data[0]
	}

	return c.Context.JSON(m)
}

// Success 成功的网络返回值
func (c Context) Success(message string, data ...interface{}) (int, error) {
	m := iris.Map{
		"status":    Success,
		"message":   message,
		"timestamp": time.Now(),
	}

	if data != nil && len(data) != 0 {
		m["data"] = data[0]
	}

	return c.Context.JSON(m)
}

const errorDataKey = "__this is the error data__"

// Raise 抛出错误
func (c Context) Raise(v ...interface{}) {
	var err Error

	if v == nil || len(v) == 0 {
		err = NewError(ServerError, "服务异常")
	} else {
		for _, _v := range v {
			switch e := _v.(type) {
			case Error:
				err = e
				goto PANIC
			case error:
				err = NewError(ServerError, e.Error())
				goto PANIC
			case int:
				err.Code = e
			case string:
				err.Message = e
			case ErrorData:
				c.Values().Set(errorDataKey, e)
			}
		}
	}
PANIC:
	panic(err)
}

// CheckRaise 检查并抛出错误
func (c Context) CheckRaise(errOrBool interface{}, v ...interface{}) {
	if errOrBool != nil {
		var coreErr Error

		switch e := errOrBool.(type) {
		case Error:
			coreErr = e
		case error:
			coreErr.Code = ServerError
			coreErr.Message = e.Error()
		case bool:
			if !e {
				return
			}
			coreErr.Code = ServerError
		default:
			panic("CheckRaise 不支持的类型")
		}

		if v != nil && len(v) != 0 {
			for _, _v := range v {
				switch e := _v.(type) {
				case int:
					coreErr.Code = e
				case string:
					coreErr.Message = e
				case ErrorData:
					c.Values().Set(errorDataKey, e)
				}
			}
		}

		panic(coreErr)
	}
}

// GetErrorData 获取错误数据
func (c Context) GetErrorData() ErrorData {
	e := c.Values().Get(errorDataKey)
	if e == nil {
		return ErrorData{}
	} else {
		return e.(ErrorData)
	}
}

// Verify 校验参数，若参数为空则自动检验json参数
func Verify(v ...interface{}) error {
	var _v interface{}

	if v == nil || len(v) == 0 {
		return NewError(Fail, "校验并未找到相关参数")
	}

	_v = v[0]

	rt := reflect.TypeOf(_v)
	rv := reflect.ValueOf(_v)

	if rv.Kind() == reflect.Ptr {
		rv = rv.Elem()
		rt = reflect.TypeOf(rv.Interface())
	}

	fieldsLen := rv.NumField()

	for i := 0; i < fieldsLen; i++ {
		var value interface{}
		value = rv.Field(i).Interface()
		typeFiled := rt.Field(i)

		verifyTag := typeFiled.Tag.Get("verify")

		if verifyTag != "" {
			msgTag := typeFiled.Tag.Get("msg")
			codeTag := typeFiled.Tag.Get("code")
			jsonTag := typeFiled.Tag.Get("json")

			if codeTag == "" {
				codeTag = "400"
			}
			status, err := strconv.ParseInt(codeTag, 10, 32)
			if err != nil {
				return NewError(ServerError, typeFiled.Name+"的code标签设置不正确")
			}
			success, err := validate.Check(value, verifyTag)
			if err != nil {
				return NewError(ServerError, err.Error())
			}
			if !success {
				if msgTag != "" {
					return NewError(int(status), msgTag)
				}
				return NewError(int(status), jsonTag+"验证失败")
			}
		}
	}
	return nil
}
